%%%-------------------------------------------------------------------
%%% @author Anil Kumar S. R. <sranil@gmail.com>
%%% @copyright (C) 2010, 
%%% @doc
%%%
%%% @end
%%% Created : 21 Oct 2010 by Anil Kumar S. R. <sranil@gmail.com>
%%%-------------------------------------------------------------------
-module(uas_core).
-export([packet/1]).

-include("../include/sip.hrl").

packet([general, Status_Code, Status_Desc, Parse_Record, SIP_Session_State]) ->
    #sip_session_state{transaction_table = Transaction_Table} = SIP_Session_State,
    #parse_record{via = Via_List, branch_id = Branch_ID, cseq = CSeq_Num, method = Method, from = From_URI, from_tag = From_Tag, to = To_URI, to_tag = To_Tag, call_id = Call_ID_Str} = Parse_Record,
    
    Status_Line = status_line(Status_Code, Status_Desc),
    Via = via(Via_List),
    From = from(From_URI, From_Tag),
    case Status_Code of
        "101" ->
            New_To_Tag = "",
            To = to(To_URI);
        _ ->
            if
                To_Tag == "" ->
                    New_To_Tag = utils:generate_random_number(9999999999),
                    To = to(To_URI, New_To_Tag);
                true ->
                    New_To_Tag = To_Tag,
                    To = to(To_URI, New_To_Tag)
            end
    end,

    Call_ID = callid(Call_ID_Str),
    CSeq = cseq(CSeq_Num, Method),
    User_Agent = user_agent(),
    Content_Length = content_length(0),
    
    ets:insert(Transaction_Table, #transaction{branch_id = Branch_ID, method = Status_Code, cseq = CSeq_Num, call_id = Call_ID_Str}),

    Response = [Status_Line, Via, From, To, Call_ID, CSeq, User_Agent, Content_Length, "\r\n"],
    {Response, Parse_Record#parse_record{to_tag = New_To_Tag}};

packet([before_dialog, Status_Code, Status_Desc, Parse_Record, SIP_Session_PID, SIP_Session_State]) ->
    #sip_session_state{userid = Userid, mapped_address = Mapped_Address, transaction_table = Transaction_Table} = SIP_Session_State,
    #parse_record{via = Via_List, route = Route_List, branch_id = Branch_ID, cseq = CSeq_Num, method = Method, from = From_URI, from_tag = From_Tag, to = To_URI, to_tag = To_Tag, call_id = Call_ID_Str, contact = Contact_List, sdp = SDP_Record} = Parse_Record,

    Status_Line = status_line(Status_Code, Status_Desc),
    Via = via(Via_List),
    
    if
        Route_List /= [] ->
            Route = route(Route_List);
        true ->
            Route = ""
    end,

    From = from(From_URI, From_Tag),
    To = to(To_URI, To_Tag),
    Call_ID = callid(Call_ID_Str),

    CSeq = cseq(CSeq_Num, Method),

    User_Agent = user_agent(),
    Contact = contact(Userid, Mapped_Address),
    Max_Forwards = max_forwards(),

    if
        Status_Code == "200" andalso Method == "INVITE" ->
            case catch media_session:start([SIP_Session_PID]) of
                {ok, Media_Session_PID} ->
                    case catch gen_server:call(Media_Session_PID, create_media_socket, infinity) of
                        {ok, RTP_Port} ->
                            #contact_record{contact = Peer_Contact} = hd(Contact_List),
                            #sdp{peer_port = Peer_Port, media = Media} = SDP_Record,
                            
                            [_, T1] = re:split(Peer_Contact, "@", [{return, list}]),
                            Peer_IP = re:replace(T1, "\\>", "", [{return, list}]),
                            
                            gen_server:cast(Media_Session_PID, {set_peer_address_and_media, {Peer_IP, Peer_Port}, Media}),

                            SDP_Data = generate_sdp([Userid, RTP_Port]),
                            
                            Content_Type = content_type("application/sdp"),
                            
                            Content_Length = content_length(length(SDP_Data)),
                            
                            Response = [Status_Line, Via, Route, From, To, CSeq, Call_ID, Contact, Max_Forwards, User_Agent, Content_Type, Content_Length, "\r\n", SDP_Data],
                            %%ets:insert(Transaction_Table, #transaction{branch_id = Branch_ID, method = Method, cseq = CSeq_Num, call_id = Call_ID_Str}),
                            {Branch_ID, Response, SIP_Session_State#sip_session_state{media_session_pid = Media_Session_PID}};
                        {error, Error_Code} ->
                            gen_server:cast(Media_Session_PID, kill_process),
                            {error, Error_Code};
                        _ ->
                            gen_server:cast(Media_Session_PID, kill_process),
                            {error, "106"}
                    end;
                _ ->
                    {error, "106"}
            end;
        true ->
            Content_Length = content_length(0),
            Response = [Status_Line, Via, Route, From, To, CSeq, Call_ID, Contact, Max_Forwards, User_Agent, Content_Length, "\r\n"],
            ets:insert(Transaction_Table, #transaction{branch_id = Branch_ID, method = Method, cseq = CSeq_Num, call_id = Call_ID_Str}),
            {Branch_ID, Response, SIP_Session_State}
    end;

packet([after_dialog, Status_Code, Status_Desc, Wait_For_Response, Dialog_State, Parse_Record, SIP_Session_State]) ->
    #sip_session_state{transaction_table = Transaction_Table} = SIP_Session_State,
    #parse_record{via = Via_List, branch_id = Branch_ID, cseq = CSeq_Num, method = Method, from = From_URI, from_tag = From_Tag, to = To_URI, to_tag = To_Tag, call_id = Call_ID_Str} = Parse_Record,
    
    #dialog{route = Route_List} = Dialog_State,

    Status_Line = status_line(Status_Code, Status_Desc),
    Via = via(Via_List),
    
    if
        Route_List /= [] ->
            Route = route(Route_List);
        true ->
            Route = ""
    end,

    From = from(From_URI, From_Tag),
    To = to(To_URI, To_Tag),
    Call_ID = callid(Call_ID_Str),

    CSeq = cseq(CSeq_Num, Method),

    User_Agent = user_agent(),
    Max_Forwards = max_forwards(),

    Content_Length = content_length(0),
    Response = [Status_Line, Via, Route, From, To, CSeq, Call_ID, Max_Forwards, User_Agent, Content_Length, "\r\n"],
    if 
        Wait_For_Response == true ->
            ets:insert(Transaction_Table, #transaction{branch_id = Branch_ID, method = Method, cseq = CSeq_Num, call_id = Call_ID_Str});
        true ->
            nothing
    end,
    {Branch_ID, Response}.
                        
status_line(Status_Code, Status_Desc) ->
    ["SIP/2.0", $\s, Status_Code, $\s, Status_Desc, "\r\n"].

via(Via_List) ->
    Via = 
        lists:foldl(
          fun(X, Y) ->
                  Y ++ ["Via:", $\s, X, "\r\n"]
          end, [], Via_List),
    Via.

route(Route_List) ->
    lists:foldl(
      fun(X, Y) ->
              ["Record-Route:", $\s, X, "\r\n"] ++ Y
      end, [], Route_List).

from(From_URI, From_Tag) when is_integer(From_Tag) ->
    ["From:", $\s, From_URI, $;, "tag=", integer_to_list(From_Tag), "\r\n"];
from(From_URI, From_Tag) ->
    ["From:", $\s, From_URI, $;, "tag=", From_Tag, "\r\n"].

to(To_URI) ->
    ["To:", $\s, To_URI, "\r\n"].

to(To_URI, To_Tag) when is_integer(To_Tag) ->
    ["To:", $\s, To_URI, $;, "tag=", integer_to_list(To_Tag), "\r\n"];
to(To_URI, To_Tag) ->
    ["To:", $\s, To_URI, $;, "tag=", To_Tag, "\r\n"].

cseq(CSeq_Num, Method) when is_list(CSeq_Num) ->
    ["CSeq:", $\s, CSeq_Num, $\s, Method, "\r\n"];

cseq(CSeq_Num, Method) ->
    ["CSeq:", $\s, integer_to_list(CSeq_Num), $\s, Method, "\r\n"].

callid(Call_ID_Str) ->
    ["Call-ID:", $\s, Call_ID_Str, "\r\n"].

max_forwards() ->
    ["Max-Forwards:", $\s, "70", "\r\n"].

contact(Userid, Mapped_Address) ->
    {IP, Port} = Mapped_Address,
    ["Contact:", $\s, "<sip:", Userid, $\@, IP, $:, integer_to_list(Port), $>, "\r\n"].

user_agent() ->
    ["User-Agent:", $\s, ?USER_AGENT, "\r\n"].

content_length(Length) ->
    ["Content-Length:", $\s, integer_to_list(Length), "\r\n"].

content_type(Type) ->
    ["Content-Type:", $\s, Type, "\r\n"].

generate_sdp([Userid, RTP_Port]) ->
    case catch inet:getif() of
        {ok, Network_List} ->
            IP_List = 
                [ inet_parse:ntoa(element(1, X)) || X <- Network_List, 
                       case catch inet_parse:ntoa(element(1, Network_List)) of
                           "127.0.0.1" -> false;
                           _ -> true
                       end ],
            if
                length(IP_List) > 0 -> IP = hd(IP_List);
                true -> IP = ""
            end;
        _ ->
            IP = ""
    end,
    Version = version(),
    Session_ID = utils:generate_random_number(16#FFFFF),
    Session_Ver = utils:generate_random_number(16#FFFFF),
    Origin = origin([Userid, IP, Session_ID, Session_Ver]),
    Session_Name = session_name(),
    Connection_Info = connection_info(IP),
    Time = time_info(),
    Media = media(RTP_Port),
    Media_Attributes = 
        lists:foldl(
          fun(X, Y) ->
                  case X of
                      111 ->
                          Y ++ media_attr([rtpmap, "111", "Speex", "16000", "1"]);
                      110 ->
                          Y ++ media_attr([rtpmap, "110", "Speex", "8000", "1"]);
                      _ ->
                          Y
                  end
          end, [], ?MEDIA_LIST),
    lists:flatten([Version, Origin, Session_Name, Connection_Info, Time, Media, Media_Attributes]).

version() ->
    ["v=0", "\r\n"].

origin([Username, IP, Session_ID, Session_Ver]) ->
    ["o=", Username, $\s, integer_to_list(Session_ID), $\s, integer_to_list(Session_Ver), $\s, "IN", $\s, "IP4", $\s, IP, "\r\n"].

session_name() ->
    ["s=P2P Voice Chat", "\r\n"].

connection_info(IP) ->
    ["c=IN IP4", $\s, IP, "\r\n"].

time_info() ->
    ["t=0 0", "\r\n"].

media(RTP_Port) ->
    Media_List = 
        lists:foldl(
          fun(X, Y) ->
                  if 
                      Y == [] ->
                          integer_to_list(X);
                      true ->
                          Y ++ " " ++ integer_to_list(X)
                  end
          end, [], ?MEDIA_LIST),
    ["m=audio", $\s, integer_to_list(RTP_Port), $\s, "RTP/AVP", $\s, Media_List, "\r\n"].

media_attr([rtpmap, Payload_Type, Encoding_Name, Clock_Rate, Channel_Num]) ->
    ["a=", "rtpmap:", Payload_Type, $\s, Encoding_Name, $/, Clock_Rate, $/, Channel_Num, "\r\n"].
